package com.hospital.register.cmn.controller;

import com.alibaba.excel.EasyExcel;
import com.hospital.register.cmn.excel.listener.DictReadListener;
import com.hospital.register.cmn.service.DictService;
import com.hospital.register.common.result.Result;
import com.hospital.register.dto.cmn.DictExcelDto;
import com.hospital.register.dto.cmn.DictNameQueryDto;
import com.hospital.register.vo.cmn.DictChildVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 处理字典相关请求
 *
 * @author 刘逸晖
 */
@Api(tags = "字典管理", description = "面向管理员。没有更新和删除的接口。")
@RestController
@RequestMapping("/admin/cmn/dict")
@Validated
//@CrossOrigin
public class DictAdminController {

    /**
     * 处理字典相关业务
     */
    @Autowired
    private DictService dictService;

    @ApiOperation(value = "根据 id 查询子节点列表")
    @ApiImplicitParam(name = "id", value = "父 id", required = true, paramType = "path", dataTypeClass = Long.class)
    @GetMapping("/findChildData/{id}")
    public Result<List<DictChildVo>> findChildData(@PathVariable @Min(value = 1, message = "id 非法") Long id)
            throws ExecutionException, InterruptedException, TimeoutException {
        List<DictChildVo> children = this.dictService.selectByParentId(id);
        this.markToHaveChildren(children);
        return Result.ok(children);
    }

    @ApiOperation(value = "根据编码获取子节点列表")
    @ApiImplicitParam(name = "dictCode", value = "字典的编码", dataTypeClass = String.class, paramType = "path", required = true)
    @GetMapping("/findByDictCode/{dictCode}")
    public Result<List<DictChildVo>> findByDictCode(@PathVariable String dictCode)
            throws ExecutionException, InterruptedException, TimeoutException {
        List<DictChildVo> children = this.dictService.selectChildrenByCode(dictCode);
        this.markToHaveChildren(children);
        return Result.ok(children);
    }

    @ApiOperation(value = "根据编码获取子节点 map", notes = "key 为值")
    @ApiImplicitParam(name = "parentCode", value = "字典的编码", dataTypeClass = String.class, paramType = "path", required = true)
    @GetMapping("/findValueMap/{parentCode}")
    public Result<Map<String, DictChildVo>> findValueMap(@PathVariable String parentCode)
            throws ExecutionException, InterruptedException, TimeoutException {
        List<DictChildVo> children = this.dictService.selectChildrenByCode(parentCode);
        this.markToHaveChildren(children);

        Map<String, DictChildVo> codeAndVos = new HashMap<>(children.size());
        for (DictChildVo vo : children) {
            codeAndVos.put(vo.getValue(), vo);
        }
        return Result.ok(codeAndVos);
    }

    @ApiOperation(value = "导入字典", notes = "父节点在前，子节点在后；只能新增，不能修改")
    @ApiImplicitParam(name = "file", value = "Excel 文件")
    @PostMapping("/importData")
    public Result importDict(MultipartFile file)
            throws IOException {
        //使用监听器将 Excel 中的数据读取并写入数据库
        DictReadListener readListener = new DictReadListener(this.dictService, 2);
        EasyExcel.read(file.getInputStream(), DictExcelDto.class, readListener).sheet().doRead();
        return Result.ok();
    }

    @ApiOperation(value = "导出字典")
    @GetMapping("exportData")
    public void exportDict(@ApiIgnore HttpServletResponse response)
            throws IOException {
        //设置响应头
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition", "attachment;filename=dictList.xlsx");

        //查询数据
        List<DictExcelDto> excelData = this.dictService.selectExcelData();

        if (excelData == null) {
            excelData = Collections.emptyList();
        }

        //导出成 Excel
        EasyExcel.write(response.getOutputStream(), DictExcelDto.class).sheet("字典信息").doWrite(excelData);
    }

    @ApiOperation(value = "根据字典编码和字典值获取字典名称")
    @ApiImplicitParams(value = {
            @ApiImplicitParam(name = "dictCode", value = "字典编码", paramType = "path", dataTypeClass = String.class, required = true),
            @ApiImplicitParam(name = "value", value = "字典值", paramType = "path", dataTypeClass = Long.class, required = true)
    })
    @GetMapping("/getName/{dictCode}/{value}")
    public String getName(
            @PathVariable @NotBlank(message = "字典编码不可为空") String dictCode,
            @PathVariable @NotNull(message = "字典值不可为空") Long value) {
        DictNameQueryDto dto = new DictNameQueryDto();
        dto.setDictCode(dictCode);
        dto.setValue(value);
        return this.dictService.selectName(dto);
    }

    @ApiOperation(value = "根据字典值查询字典名称")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "value", value = "字典值", paramType = "path", dataTypeClass = Long.class, required = true)
    })
    @GetMapping("/getName/{value}")
    public String getName(
            @PathVariable @NotNull(message = "字典值不可为空") Long value) {
        DictNameQueryDto dto = new DictNameQueryDto();
        dto.setValue(value);
        return this.dictService.selectName(dto);
    }

    /**
     * 标记多个字典是否有子节点
     *
     * @param children 需要标记的字典
     * @throws ExecutionException
     * @throws InterruptedException
     * @throws TimeoutException
     */
    private void markToHaveChildren(List<DictChildVo> children) throws ExecutionException, InterruptedException, TimeoutException {
        List<Future<Boolean>> futureResults = new ArrayList<>(children.size());
        for (DictChildVo child : children) {//并发填充每一个字典的 hasChildren 属性，成功返回 true
            Future<Boolean> futureResult = this.dictService.hasChildren(child);
            futureResults.add(futureResult);
        }

        for (Future<Boolean> futureResult : futureResults) {//阻塞等待每一个任务执行完成
            futureResult.get(2L, TimeUnit.SECONDS);
        }
    }

}
